import {
  // camel2snakeJson,
  snake2camelJson
  // getOffsetDate,
  // getFullTimeStr
} from '../../shared/index'
import crypto from 'crypto'
import querystring from 'querystring'
import AlipayBase from 'alipay-sdk'
import protocols from './protocols'

import {
  sign
} from 'alipay-sdk/lib/util'

const ALIPAY_ALGORITHM_MAPPING = {
  RSA: 'RSA-SHA1',
  RSA2: 'RSA-SHA256'
}
export default class Payment extends AlipayBase {
  constructor (options) {
    if (options.sandbox) {
      options.gateway = 'https://openapi.alipaydev.com/gateway.do'
    }
    options = Object.assign({
      gateway: 'https://openapi.alipay.com/gateway.do',
      timeout: 5000,
      charset: 'utf-8',
      version: '1.0',
      signType: 'RSA2',
      timeOffset: 8, // 以小时为单位，想获取utc+x的时间就传x
      keyType: 'PKCS8'
    }, options)
    super(options)
    this.options = options
    this._protocols = protocols
  }

  async _request (method, params) {
    const data = {}
    if (params.notifyUrl) {
      data.notifyUrl = params.notifyUrl
      delete params.notifyUrl
    }
    data.bizContent = params
    const result = await this.exec(method, data, {
      validateSign: true
    })
    return result
  }

  // 小程序支付时seller_id、buyer_id都是必填项
  async unifiedOrder (params) {
    const result = await this._request(
      'alipay.trade.create',
      Object.assign({ sellerId: this.options.mchId }, params)
    )
    return result
  }

  _getSign (method, params) {
    return sign(method, params, this.config)
  }

  // 覆盖alipay-sdk内的formatUrl方法，修正app_cert_sn、alipay_root_cert_sn为蛇形参数时报错的问题
  // 需要注意的时支付宝alipay-sdk内部有使用驼峰形式的appCertSn、alipayRootCertSn，所以两种形式参数同时保留
  formatUrl (url, params) {
    let requestUrl = url
    // 需要放在 url 中的参数列表
    const urlArgs = [
      'app_id', 'method', 'format', 'charset',
      'sign_type', 'sign', 'timestamp', 'version',
      'notify_url', 'return_url', 'auth_token', 'app_auth_token',
      'app_cert_sn', 'alipay_root_cert_sn',
      'appCertSn', 'alipayRootCertSn'
    ]
    for (const key in params) {
      if (urlArgs.indexOf(key) > -1) {
        const val = encodeURIComponent(params[key])
        requestUrl = `${requestUrl}${requestUrl.includes('?') ? '&' : '?'}${key}=${val}`
        // 删除 postData 中对应的数据
        delete params[key]
      }
    }
    return { execParams: params, url: requestUrl }
  }

  async getOrderInfo (params) {
    let tradeType
    if (params.tradeType) {
      tradeType = params.tradeType
      delete params.tradeType
    } else {
      switch (this.options.clientType) {
        case 'app-plus':
        case 'app':
          tradeType = 'APP'
          break
        case 'mp-alipay':
        default:
          tradeType = 'JSAPI'
          break
      }
    }
    switch (tradeType) {
      case 'APP': {
        delete params.buyerId
        const data = {}
        if (params.notifyUrl) {
          data.notifyUrl = params.notifyUrl
          delete params.notifyUrl
        }
        data.bizContent = params
        const signData = this._getSign('alipay.trade.app.pay', data)
        const { url, execParams } = this.formatUrl('', signData)
        const orderInfo = (
          url +
          '&biz_content=' +
          encodeURIComponent(execParams.biz_content)
        ).substr(1)
        return orderInfo
      }
      case 'JSAPI': {
        const orderResult = await this.unifiedOrder(params)
        if (!orderResult.tradeNo) {
          // {
          //   "code": "40002",
          //   "msg": "Invalid Arguments",
          //   "subCode": "isv.invalid-signature",
          //   "subMsg": "验签出错，建议检查签名字符串或签名私钥与应用公钥是否匹配，网关生成的验签字符串为：xxx"
          // }
          throw new Error('获取支付宝交易号失败，详细信息为：' + JSON.stringify(orderResult))
        }
        return orderResult.tradeNo
      }
      case 'NATIVE': {
        const orderResult = await this._request(
          'alipay.trade.precreate',
          Object.assign({ sellerId: this.options.mchId }, params)
        )
        return orderResult
      }
      default: {
        throw new Error(
          '不支持的支付类型，支付宝支付下单仅支持App、支付宝小程序、网站二维码支付'
        )
      }
    }
  }

  async orderQuery (params) {
    const result = await this._request('alipay.trade.query', params)
    return result
  }

  async cancelOrder (params) {
    const result = await this._request('alipay.trade.cancel', params)
    return result
  }

  async closeOrder (params) {
    const result = await this._request('alipay.trade.close', params)
    return result
  }

  async refund (params) {
    const result = await this._request('alipay.trade.refund', params)
    return result
  }

  async refundQuery (params) {
    const result = await this._request(
      'alipay.trade.fastpay.refund.query',
      params
    )
    return result
  }

  notifyRSACheck (signArgs, signStr, signType) {
    const signContent = Object.keys(signArgs).sort().filter(val => val).map((key) => {
      let value = signArgs[key]
      if (Array.prototype.toString.call(value) !== '[object String]') {
        value = JSON.stringify(value)
      }
      return `${key}=${value}`
    }).join('&')
    const verifier = crypto.createVerify(ALIPAY_ALGORITHM_MAPPING[signType])
    return verifier.update(signContent, 'utf8').verify(this.config.alipayPublicKey, signStr, 'base64')
  }

  _getNotifyData (notify) {
    if (!notify.headers) {
      throw new Error('通知格式不正确')
    }
    let contentType
    for (const key in notify.headers) {
      if (key.toLowerCase() === 'content-type') {
        contentType = notify.headers[key]
      }
    }
    if (notify.isBase64Encoded !== false && contentType.indexOf('application/x-www-form-urlencoded') === -1) {
      throw new Error('通知格式不正确')
    }
    const postData = querystring.parse(notify.body)
    return postData
  }

  _verifyNotify (notify) {
    const postData = this._getNotifyData(notify)
    if (this.checkNotifySign(postData)) {
      return snake2camelJson(postData)
    }
    return false
  }

  // {"gmt_create":"2020-05-09 10:59:00","charset":"utf-8","seller_email":"payservice@dcloud.io","subject":"DCloud项目捐赠","sign":"fZyNcBGZHUerYrDApdsDaMosoNk/FxMLHDmtheHu9MVsMkLaAz+uJcLA8rSiSP7sT0ajevzNKAoqXnJUkf289NTpSGsEG9sb428k8gAeuQH+8c1XOoPIs4KYRTJkV67F+vQvhlV6r/aSzW2ygJHQ92osHTEPfsHNQKfegFTAJJFES8vgNOV1LkOJZtmFjNxoYS5Z0cwVgrpl/+5avrVNlNIfEbF6VZ8sHNRxycOY7OwJ7QcjTi6qRZqRahtj3wKeFGVmVgsUaixqm4ctw2dy1VjYBWZ6609vfVA9i2Nnkyhoy4pjlWnFKiwt9q3s8rwiiCY22uvqcqWbB30WIJDraw==","buyer_id":"2088********0430","body":"DCloud致力于打造HTML5最好的移动开发工具，包括终端的Runtime、云端的服务和IDE，同时提供各项配套的开发者服务。","invoice_amount":"0.01","notify_id":"2020050900222110211000431413435818","fund_bill_list":"[{\"amount\":\"0.01\",\"fundChannel\":\"ALIPAYACCOUNT\"}]","notify_type":"trade_status_sync","trade_status":"TRADE_SUCCESS","receipt_amount":"0.01","buyer_pay_amount":"0.01","app_id":"2018********5175","sign_type":"RSA2","seller_id":"2088********6834","gmt_payment":"2020-05-09 11:02:11","notify_time":"2020-05-09 14:29:02","version":"1.0","out_trade_no":"1588993139851","total_amount":"0.01","trade_no":"2020050922001400431443283406","auth_app_id":"2018********5175","buyer_logon_id":"188****5803","point_amount":"0.00"}
  // 上述信息中的seller_id、buyer_id、app_id、auth_app_id去除了中间8位，实际是正常的id
  verifyPaymentNotify (notify) {
    if (this.checkNotifyType(notify) !== 'payment') {
      return false
    }
    return this._verifyNotify(notify)
  }

  verifyRefundNotify (notify) {
    if (this.checkNotifyType(notify) !== 'refund') {
      return false
    }
    return this._verifyNotify(notify)
  }

  checkNotifyType (notify) {
    const postData = this._getNotifyData(notify)
    if ('refund_fee' in postData) {
      return 'refund'
    }
    return 'payment'
  }
}
